package com.ajaxjs.user.password;

import java.io.IOException;
import java.nio.charset.StandardCharsets;
import java.security.InvalidKeyException;
import java.security.NoSuchAlgorithmException;
import java.security.SecureRandom;

import javax.crypto.*;

import sun.misc.BASE64Decoder;
import sun.misc.BASE64Encoder;

/**
 * 
 * @author Frank Cheung frank@ajaxjs.com
 * @version 2017年7月16日 下午10:43:13
 */
public class Encryption {
	/**
	 * DES = 56 | AES = 128
	 */
	private int keySize;

	/**
	 * 加密算法，可以 DES | AES
	 */
	private String ALGORITHM;

	/**
	 * 加密
	 * 
	 * @param str
	 *            要加密的内容
	 * @param key
	 *            密钥
	 * @return 加密后的内容
	 */
	public String encrypt(String str, String key) {
		Cipher cipher = null;

		try {
			cipher = Cipher.getInstance(ALGORITHM);
			// (这里要设置为utf-8)不然内容中如果有中文和英文混合中文就会解密为乱码
			cipher.init(Cipher.ENCRYPT_MODE, generateKey(key));
		} catch (NoSuchAlgorithmException | NoSuchPaddingException | InvalidKeyException e) {
			e.printStackTrace();
			return null;
		}

		try {
			// 为了防止解密时报javax.crypto.IllegalBlockSizeException: Input length must
			// be multiple of 8 when decrypting with padded cipher异常，
			// 不能把加密后的字节数组直接转换成字符串
			byte[] buf = cipher.doFinal(str.getBytes(StandardCharsets.UTF_8));
			return base64Encode(buf);
		} catch (IllegalBlockSizeException | BadPaddingException e) {
			e.printStackTrace();
			return null;
		}
	}

	/**
	 * 解密
	 * 
	 * @param str
	 *            要解密的内容
	 * @param key
	 *            密钥
	 * @return 解密后的内容
	 */
	public String decrypt(String str, String key) {
		Cipher cipher = null;

		try {
			cipher = Cipher.getInstance(ALGORITHM);
			cipher.init(Cipher.DECRYPT_MODE, generateKey(key));
		} catch (NoSuchAlgorithmException | InvalidKeyException | NoSuchPaddingException e) {
			e.printStackTrace();
			return null;
		}

		try {
			byte[] buf = cipher.doFinal(base64Decode(str));
			return byte2String(buf);
		} catch (IllegalBlockSizeException | BadPaddingException e) {
			e.printStackTrace();
			return null;
		}
	}

	/**
	 * 获得密钥对象
	 * 
	 * @param key
	 *            密钥
	 * @return 密钥对象
	 */
	private SecretKey generateKey(String key) {
		try {
			SecureRandom secureRandom = SecureRandom.getInstance("SHA1PRNG");
			secureRandom.setSeed(key.getBytes());
			KeyGenerator kg = KeyGenerator.getInstance(ALGORITHM);
			kg.init(getKeySize(), secureRandom);

			// 生成密钥
			return kg.generateKey();
		} catch (NoSuchAlgorithmException e) {
			e.printStackTrace();
			return null;
		}
	}

	public int getKeySize() {
		return keySize;
	}

	public void setKeySize(int keySize) {
		this.keySize = keySize;
	}

	public String getALGORITHM() {
		return ALGORITHM;
	}

	public void setALGORITHM(String aLGORITHM) {
		ALGORITHM = aLGORITHM;
	}

	public static String base64Encode(byte[] data) {
		return new String(new BASE64Encoder().encode(data));
	}

	/**
	 * BASE64 解码 这里需要强制捕获异常。
	 * 中文乱码：http://s.yanghao.org/program/viewdetail.php?i=54806
	 * 
	 * @param str
	 *            已解码的字符串
	 * @return 已解码的字符串
	 */
	public static byte[] base64Decode(String str) {
		BASE64Decoder decoder = new BASE64Decoder();

		try {
			return decoder.decodeBuffer(str);
		} catch (IOException e) {
			e.printStackTrace();
			return null;
		}
	}

	/**
	 * 字节转为 UTF-8 字符串
	 * 
	 * @param bytes
	 *            输入字符串的字节数组
	 * @return UTF-8 字符串
	 */
	public static String byte2String(byte[] bytes) {
		return new String(bytes, StandardCharsets.UTF_8);
	}

	
	//////
	
	public static class DES extends Encryption {
		public DES() {
			setALGORITHM("DES");
			setKeySize(56);
		}
	}

	final static Encryption AES = new Encryption();
	static {
		AES.setALGORITHM("AES");
		AES.setKeySize(128);
	}

	/**
	 * AES 加密
	 * 
	 * @param str
	 *            要加密的内容
	 * @param key
	 *            密钥
	 * @return 加密后的内容
	 */
	public static String AES_Encrypt(String str, String key) {
		return AES.encrypt(str, key);
	}

	/**
	 * AES 解密
	 * @param str
	 * @param key
	 * @return
	 */
	public static String AES_Decrypt(String str, String key) {
		return AES.decrypt(str, key);
	}
}
